package blockr.function;

import blockr.model.RandomNumOBJ;
import blockr.util.Byte_SHIFT;
import blockr.util.Byte_XOR;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

/**
 * @author spencercjh
 */
public class Do_Encrypt {
    private static RandomNumOBJ[] All_Random_Num = null;
    private static int arraySize;

    public static byte[][][] DoEncrypt(byte[][] All_Piece, byte[][][] All_Block, int G, RandomNumOBJ firstBlockKey, int array_size) throws Exception {
        arraySize = array_size;
        Do_Encrypt_First_Part(All_Piece, All_Block, G, firstBlockKey);
        Do_Encrypt_Other_Part(All_Block, G);
        return All_Block;
    }

    public static void Do_Encrypt_First_Part(byte[][] All_Piece, byte[][][] All_Block, int G, RandomNumOBJ firstBlockKey) {
        All_Random_Num = new RandomNumOBJ[MappedBiggerFileReader.getBlock_index() + 1];
        All_Random_Num[0] = firstBlockKey;
        long start_encrypt_first = System.currentTimeMillis();
        if (MappedBiggerFileReader.isAdd()) {
            if (MappedBiggerFileReader.getPiece_index() % (arraySize * 8) == 0) {    //总片数正好是arraySize*8的整倍数，新加片和原最后一片不在同一块上
                EncryptForEndPiece_unnormal(All_Piece, All_Block, All_Random_Num[0]);
            } else {
                EncryptForEndPiece_normal(All_Piece, All_Block, All_Random_Num[0], All_Piece.length - 2);
                EncryptForEndPiece_normal(All_Piece, All_Block, All_Random_Num[0], All_Piece.length - 1);
            }
        } else {
            EncryptForEndPiece_normal(All_Piece, All_Block, All_Random_Num[0], All_Piece.length - 1);
        }
//        byte[][][] All_Block = Split_Block.Split_PieceToBlock(All_Piece);
        EncryptForFirstBlock(All_Block, All_Random_Num[0], G);
        long end_encrypt_first = System.currentTimeMillis();
        System.out.println("加密最后片和第一块耗时：" + String.valueOf(end_encrypt_first - start_encrypt_first));
//        return All_Block;
    }

    public static void Do_Encrypt_Other_Part(byte[][][] All_Block, int G) throws Exception {
        long start_encrypt_other = System.currentTimeMillis();
        for (int i = 1; i <= MappedBiggerFileReader.getBlock_index(); i++) {    //从第一块开始遍历
            byte[][] index_block = All_Block[i];    //第i块
            if (index_block == null) {
                break;
            } else {
                int temp = arraySize / 2;
                int x = (17 * i + G) % temp;   //(17*(file_num+G))mod512    i是片数
                int y = (13 * (i + G) + i) % temp + temp; //(13*(file_num+G)+file_num)mod512+512
                All_Random_Num[i] = new RandomNumOBJ(RandomNum.generate_block_random_num_1(All_Block[i - 1][x],
                        All_Random_Num[i - 1].getR1(), x, arraySize), RandomNum.generate_block_random_num_2(All_Block[i - 1][y],
                        All_Random_Num[i - 1].getR2(), y, arraySize));
//                All_Random_Num[file_num] = new RandomNumOBJ(RandomNum.generate_test_random_num(128), RandomNum.generate_test_random_num(128));
//                byte[] key = Byte_XOR.XOR(All_Random_Num[file_num].getR1(), All_Random_Num[file_num].getR2());
                int end = 0;
                if (i == MappedBiggerFileReader.getBlock_index()) {    //如果此块是最后一块则不加密最后一片
                    end = MappedBiggerFileReader.getPiece_index() % (arraySize * 8 - 1);
                    if (end < 0) {
                        end = index_block.length;
                    }
                } else {
                    end = index_block.length;
                }
                for (int j = 0; j < end; j++) {
                    byte[] index_piece = index_block[j];    //第j片
                    if (index_piece == null) {
                        break;
                    } else {
                        byte[] index_random_num_1 = RandomNum.generate_index_random_num_1(All_Random_Num[i].getR1(), j % (arraySize * 8), arraySize);
                        int shift_X = ((G % temp) * (j * 8 + G) + 19) % temp;   //(Gmod512*(8j+G)+19)mod512   j是片数
                        byte[] index_random_num_2 = RandomNum.generate_index_random_num_2(All_Random_Num[i].getR2(), shift_X, arraySize);
                        byte[] key = Byte_XOR.XOR(index_random_num_1, index_random_num_2);
                        index_piece = Byte_XOR.XOR(index_piece, key);
                        index_block[j] = index_piece;   //替换旧片
                    }
                }
                All_Block[i] = index_block; //替换旧块
            }
        }
        long end_encrypt_other = System.currentTimeMillis();
        System.out.println("加密剩余部分耗时：" + String.valueOf(end_encrypt_other - start_encrypt_other));
    }

    private static void EncryptForEndPiece_unnormal(byte[][] All_Piece, byte[][][] All_Block, RandomNumOBJ randomNumOBJ) {
        byte[][] final_block = All_Block[MappedBiggerFileReader.getBlock_index() - 1];
        byte[] final_piece = final_block[arraySize * 8 - 1];
        byte[][] end_block = All_Block[MappedBiggerFileReader.getBlock_index()];
        byte[] end_piece = end_block[0];
        byte[] R1 = randomNumOBJ.getR1();
        R1 = Byte_SHIFT.leftShift(R1, arraySize * 8 - 1, arraySize);  //R1<<(n-1)
        byte[] R2 = randomNumOBJ.getR2();
        R2 = Byte_SHIFT.rightShift(R2, arraySize * 8 - 1, arraySize); //R2>>(n-1)
        final_piece = Byte_XOR.XOR(final_piece, R1);   //Mn-1^(R1<<(n-1))
        final_piece = Byte_XOR.XOR(final_piece, R2);   //Mn-1^(R1<<(n-1))^(R2>>(n-1))
        final_block[arraySize * 8 - 1] = final_piece;
        All_Piece[MappedBiggerFileReader.getPiece_index() - 1 - 1] = final_piece;
        All_Block[MappedBiggerFileReader.getBlock_index() - 1] = final_block;
        R1 = randomNumOBJ.getR1();
        R1 = Byte_SHIFT.leftShift(R1, 0, arraySize);  //R1<<(n)
        R2 = randomNumOBJ.getR2();
        R2 = Byte_SHIFT.rightShift(R2, 0, arraySize); //R2>>(n)
        end_piece = Byte_XOR.XOR(end_piece, R1);   //Mn-1^(R1<<(n))
        end_piece = Byte_XOR.XOR(end_piece, R2);   //Mn-1^(R1<<(n))^(R2>>(n))
        end_block[0] = end_piece;
        All_Piece[MappedBiggerFileReader.getPiece_index() - 1] = end_piece;
        All_Block[MappedBiggerFileReader.getBlock_index()] = end_block;
    }

    private static void EncryptForEndPiece_normal(byte[][] All_Piece, byte[][][] All_Block, RandomNumOBJ randomNumOBJ, int index) {
//        byte[] end_piece = All_Piece[index];
        byte[][] end_block = All_Block[MappedBiggerFileReader.getBlock_index()];
        byte[] end_piece = end_block[index % (arraySize * 8)];
        byte[] R1 = randomNumOBJ.getR1();
        R1 = Byte_SHIFT.leftShift(R1, index, arraySize);  //R1<<(n-1)
        byte[] R2 = randomNumOBJ.getR2();
        R2 = Byte_SHIFT.rightShift(R2, index, arraySize); //R2>>(n-1)
        end_piece = Byte_XOR.XOR(end_piece, R1);   //Mn-1^(R1<<(n-1))
        end_piece = Byte_XOR.XOR(end_piece, R2);   //Mn-1^(R1<<(n-1))^(R2>>(n-1))
//        All_Piece[index] = end_piece;
        end_block[index % (arraySize * 8)] = end_piece;
        All_Piece[index] = end_piece;
        All_Block[MappedBiggerFileReader.getBlock_index()] = end_block;
    }

    public static void EncryptForFirstBlock(byte[][][] All_Block, RandomNumOBJ randomNumOBJ, int G) {
        byte[][] first_block = All_Block[0];
        int end;
        if (MappedBiggerFileReader.getBlock_index() == 0) {
            end = MappedBiggerFileReader.getPiece_index() - 1;
        } else {
            end = first_block.length;
        }
        for (int i = 0; i < end; i++) {  //遍历第一块的所有片
            byte[] index_piece = first_block[i];
            if (index_piece == null) {
                break;
            } else {
                int temp = arraySize / 2;
                byte[] index_random_num_1 = RandomNum.generate_index_random_num_1(randomNumOBJ.getR1(), i % (arraySize * 8), arraySize);
                int shift_X = ((G % temp) * (i * 8 + G) + 19) % temp;   //(Gmod512*(8i+G)+19)mod512   i是片数
                byte[] index_random_num_2 = RandomNum.generate_index_random_num_2(randomNumOBJ.getR2(), shift_X, arraySize);
                byte[] key = Byte_XOR.XOR(index_random_num_1, index_random_num_2);
                index_piece = Byte_XOR.XOR(index_piece, key);
                first_block[i] = index_piece;
            }
        }
        All_Block[0] = first_block;
    }

    public static RandomNumOBJ[] getAll_Random_Num() {
        return All_Random_Num;
    }

    private static void writeKey(byte[] content, String outputfile) {
        OutputStream os;
        try {
            os = new FileOutputStream(outputfile);
            os.write(content);
            os.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

